﻿//========================================================= 
/**@file protobuf_extractor.h
 * @brief protobuf扩展
 * 
 * @date 2018-06-21   14:38:13
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_PROTOBUF_EXTRACTOR_H_
#define _LIBZHOUYB_PROTOBUF_EXTRACTOR_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"

#include "../../container/list.h"
using zhou_yb::container::_Nonconst_traits;
using zhou_yb::container::_Const_traits;

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/compiler/importer.h>

#include <memory>
//--------------------------------------------------------- 
namespace zhou_yb {
namespace wrapper {
namespace protobuf_extractor {
//--------------------------------------------------------- 
/**
 * @brief 消息数据类型萃取
 * @date 2018-07-20 15:40
 */
template<class T>
struct MessageTypeHelper { typedef T ValueType; };
/* 特化 */
#define MESSAGE_TYPE_HELPER(template_type, value_type, cpptype, api) \
template<> struct MessageTypeHelper<template_type> \
{ \
    typedef value_type ValueType; \
    static inline bool Is(const google::protobuf::FieldDescriptor* field) \
    { \
        return field->cpp_type() == google::protobuf::FieldDescriptor::cpptype; \
    } \
    static inline void Add(google::protobuf::Message& msg, \
        const google::protobuf::Reflection* ref, \
        const google::protobuf::FieldDescriptor* field, \
        const ValueType& val) \
    { \
        ref->Add##api(&msg, field, val); \
    } \
    static inline void Set(google::protobuf::Message& msg, \
        const google::protobuf::Reflection* ref, \
        const google::protobuf::FieldDescriptor* field,  \
        const ValueType& val, size_t index) \
    { \
        if(index == SIZE_EOF) \
            ref->Set##api(&msg, field, val); \
        else \
            ref->SetRepeated##api(&msg, field, index, val); \
    } \
    static inline ValueType Get(const google::protobuf::Message& msg, \
        const google::protobuf::Reflection* ref, \
        const google::protobuf::FieldDescriptor* field, size_t index) \
    { \
        if(index == SIZE_EOF) \
            return ref->Get##api(msg, field); \
        return ref->GetRepeated##api(msg, field, index); \
    } \
}
/// 嵌套的消息类型
template<> struct MessageTypeHelper<google::protobuf::Message>
{
    typedef const google::protobuf::Message* ValueType;

    static inline bool Is(const google::protobuf::FieldDescriptor* field)
    {
        return field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE;
    }
    static inline void Add(google::protobuf::Message& msg,
        const google::protobuf::Reflection* ref,
        const google::protobuf::FieldDescriptor* field,
        ValueType val)
    {
        ref->AddMessage(&msg, field)->MergeFrom(*val);
    }
    static inline void Set(google::protobuf::Message& msg,
        const google::protobuf::Reflection* ref,
        const google::protobuf::FieldDescriptor* field,
        ValueType val, size_t index)
    {
        if(index == SIZE_EOF)
            ref->MutableMessage(&msg, field)->MergeFrom(*val);
        else
            ref->MutableRepeatedMessage(&msg, field, index)->MergeFrom(*val);
    }
    static inline ValueType Get(const google::protobuf::Message& msg,
        const google::protobuf::Reflection* ref,
        const google::protobuf::FieldDescriptor* field, size_t index)
    {
        if(index == SIZE_EOF)
            return &(ref->GetMessage(msg, field));
        return &(ref->GetRepeatedMessage(msg, field, index));
    }
};
/// 枚举类型
class enum_value {};

MESSAGE_TYPE_HELPER(bool, bool, CPPTYPE_BOOL, Bool);
MESSAGE_TYPE_HELPER(double, double, CPPTYPE_DOUBLE, Double);
MESSAGE_TYPE_HELPER(float, float, CPPTYPE_FLOAT, Float);
MESSAGE_TYPE_HELPER(string, string, CPPTYPE_STRING, String);
MESSAGE_TYPE_HELPER(google::protobuf::int32, google::protobuf::int32, CPPTYPE_INT32, Int32);
MESSAGE_TYPE_HELPER(google::protobuf::uint32, google::protobuf::uint32, CPPTYPE_INT32, UInt32);
MESSAGE_TYPE_HELPER(google::protobuf::int64, google::protobuf::int64, CPPTYPE_INT32, Int64);
MESSAGE_TYPE_HELPER(google::protobuf::uint64, google::protobuf::uint64, CPPTYPE_INT32, UInt64);
MESSAGE_TYPE_HELPER(enum_value, int, CPPTYPE_ENUM, EnumValue);

#undef MESSAGE_TYPE_HELPER
//--------------------------------------------------------- 
/**
 * @brief Message字段值迭代器
 * @date 2018-07-20 15:40
 */
template<class Traits>
class MessageValueIterator
{
public:
    typedef typename Traits::value_type value_type;
    typedef MessageValueIterator<Traits> self;
    typedef MessageValueIterator<typename Traits::_NonConstTraits> iterator;
    typedef MessageValueIterator<typename Traits::_ConstTraits> const_iterator;
protected:
    const google::protobuf::Message* _message;
    const google::protobuf::FieldDescriptor* _field;
    const google::protobuf::Reflection* _ref;
    size_t _index;
    size_t _maxIndex;

    void ahead()
    {
        if(_index < _maxIndex)
        {
            ++_index;
        }
    }
    void back()
    {
        if(_index > 0)
        {
            --_index;
        }
    }
public:
    MessageValueIterator() : _index(0), _maxIndex(0), _message(nullptr), _field(nullptr), _ref(nullptr) {}
    MessageValueIterator(const google::protobuf::Message* msg,
        const google::protobuf::FieldDescriptor* field,
        const google::protobuf::Reflection* ref,
        size_t index, size_t maxIndex) : 
        _index(index), _maxIndex(maxIndex), _message(msg), _field(field), _ref(ref) {}
    MessageValueIterator(const MessageValueIterator& x) :
        _index(x._index), _maxIndex(x._maxIndex), _message(x._message), _field(x._field), _ref(x._ref) {}

    inline bool IsEof() const
    {
        return _index >= _maxIndex;
    }
    value_type operator*() const
    {
        return value_type(*_message, _field, _ref, _index);
    }
    /// ++(*this)
    self& operator++()
    {
        ahead();
        return *this;
    }
    /// (*this)++
    self operator++(int)
    {
        self temp = *this;
        ahead();
        return temp;
    }
    /// --(*this)
    self& operator--()
    {
        back();
        return *this;
    }
    /// (*this)--
    self operator--(int)
    {
        self temp = *this;
        back();
        return temp;
    }

    bool operator==(const self& x)
    {
        return (x._message == _message) && (x._field == _field) && (x._index == _index);
    }
    bool operator!=(const self& x)
    {
        return !operator==(x);
    }
};
//--------------------------------------------------------- 
/**
 * @brief Protobuf消息字段迭代器
 * @date 2018-07-20 15:41
 */
template<class Traits>
class MessageMemberItreator
{
public:
    typedef typename Traits::value_type value_type;
    typedef MessageMemberItreator<Traits> self;
    typedef MessageMemberItreator<typename Traits::_NonConstTraits> iterator;
    typedef MessageMemberItreator<typename Traits::_ConstTraits> const_iterator;
protected:
    const google::protobuf::Message* _message;
    size_t _index;
    size_t _maxIndex;

    void ahead()
    {
        if(_index < _maxIndex)
        {
            ++_index;
        }
    }
    void back()
    {
        if(_index > 0)
        {
            --_index;
        }
    }
    const google::protobuf::FieldDescriptor* get_field() const
    {
        const google::protobuf::Descriptor* d = _message->GetDescriptor();
        const google::protobuf::FieldDescriptor* field = d->field(_index);
        return field;
    }
public:
    MessageMemberItreator() : _message(nullptr), _index(0), _maxIndex(0) {}
    MessageMemberItreator(const google::protobuf::Message& msg, 
        size_t index, size_t maxIndex) : _message(&msg), _index(index), _maxIndex(maxIndex) {}
    MessageMemberItreator(const iterator& x) {}
    
    inline bool IsNull() const
    {
        return _message == nullptr;
    }
    inline bool IsEof() const
    {
        return _index >= _maxIndex;
    }
    inline void Clear()
    {
        _message->GetReflection()->ClearField(_message, get_field());
    }

    const google::protobuf::FieldDescriptor* operator->() const
    {
        return get_field();
    }
    // *this
    inline value_type operator*() const
    {
        return Value();
    }
    value_type Value() const
    {
        if(IsNull())
            return value_type();
        return value_type(*_message, get_field(), _message->GetReflection());
    }
    inline const string& Name() const
    {
        return get_field()->name();
    }
    inline const size_t Number() const
    {
        return get_field()->number();
    }
    /// ++(*this)
    self& operator++()
    {
        ahead();
        return *this;
    }
    /// (*this)++
    self operator++(int)
    {
        self temp = *this;
        ahead();
        return temp;
    }
    /// --(*this)
    self& operator--()
    {
        back();
        return *this;
    }
    /// (*this)--
    self operator--(int)
    {
        self temp = *this;
        back();
        return temp;
    }

    bool operator==(const self& x)
    {
        return (x._message == _message) && (x._index == _index);
    }
    bool operator!=(const self& x)
    {
        return !operator==(x);
    }
};
//--------------------------------------------------------- 
/**
 * @brief 消息值(提供类似于Json的访问接口)
 * @date 2018-07-20 09:26
 */
class MessageValue
{
public:
    typedef MessageValueIterator<_Nonconst_traits<MessageValue> > ConstIterator;
    typedef MessageValueIterator<_Const_traits<MessageValue> > Iterator;
protected:
    const google::protobuf::Message* _message;
    const google::protobuf::FieldDescriptor* _field;
    const google::protobuf::Reflection* _ref;
    size_t _index;

    string _toString(const google::protobuf::FieldDescriptor* field, const string& str) const
    {
        if(field->type() != google::protobuf::FieldDescriptor::TYPE_BYTES)
            return str;
        ByteBuilder buff;
        ByteConvert::ToAscii(ByteArray(str.c_str(), str.length()), buff);
        return buff.GetString();
    }
public:
    static void ObjectToString(const google::protobuf::Message& msg, string& str)
    {
        size_t fieldCount = msg.GetDescriptor()->field_count();
        MessageMemberItreator<_Const_traits<MessageValue> > itr(msg, 0, fieldCount);
        str += '{';
        while(!itr.IsEof())
        {
            MessageValue val = (*itr);
            str += val.ToString();
            str += ",";
            ++itr;
        }
        // 删除末尾多余的 ',' 号
        if(fieldCount > 0)
            str.pop_back();
        str += '}';
    }
public:
    //----------------------------------------------------- 
    MessageValue() : _message(nullptr), _field(nullptr), _ref(nullptr), _index(SIZE_EOF) {}
    MessageValue(const google::protobuf::Message& msg,
        const google::protobuf::FieldDescriptor* field,
        const google::protobuf::Reflection* ref, size_t index = SIZE_EOF) : 
        _message(&msg), _field(field), _ref(ref), _index(index) {}
    //----------------------------------------------------- 
    /* Repeated时支持的接口 */
    inline bool IsArray() const
    {
        return _field->is_repeated() && _index == SIZE_EOF;
    }
    inline size_t Index() const
    {
        return _index;
    }
    inline size_t Size() const
    {
        return _ref->FieldSize(*_message, _field);
    }
    template<class T>
    void Add(const typename MessageTypeHelper<T>::ValueType& val)
    {
        MessageTypeHelper<T>::Add(*_message, _ref, _field, val);
    }
    size_t Remove(size_t index, size_t count = 1)
    {
        size_t size = Size();
        if(index + count > size)
            count = size - index;
        size_t maxIndex = size - count;
        google::protobuf::Message* msg = const_cast<google::protobuf::Message*>(_message);
        for(size_t i = index; i < maxIndex; ++i)
        {
            _ref->SwapElements(msg, _field, i, i + count);
        }
        for(size_t i = 0; i < count; ++i)
        {
            _ref->RemoveLast(msg, _field);
        }
        return count;
    }
    /// 删除迭代器,删除后返回删除结点上一个结点的迭代器对象
    Iterator Remove(Iterator itr)
    {
        MessageValue val = (*itr);
        if(Remove(val._index) > 0)
        {
            --itr;
        }
        return itr;
    }

    Iterator Begin()
    {
        return Iterator(_message, _field, _ref, 0, Size());
    }
    ConstIterator Begin() const
    {
        return ConstIterator(_message, _field, _ref, 0, Size());
    }
    Iterator End()
    {
        size_t maxIndex = Size();
        return Iterator(_message, _field, _ref, maxIndex, maxIndex);
    }
    ConstIterator End() const
    {
        size_t maxIndex = Size();
        return ConstIterator(_message, _field, _ref, maxIndex, maxIndex);
    }
    MessageValue operator[](size_t index) const
    {
        size_t maxIndex = Size();
        if(index > maxIndex)
            index = maxIndex;
        return MessageValue(*_message, _field, _ref, index);
    }
    //----------------------------------------------------- 
    inline bool IsNull() const
    {
        return _message == nullptr || _field == nullptr || _ref == nullptr;
    }
    inline bool IsObject() const
    {
        return _field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE;
    }
    template<class T>
    bool Is() const
    {
        return MessageTypeHelper<T>::Is(_field);
    }
    template<class T>
    bool Set(const typename MessageTypeHelper<T>::ValueType& val)
    {
        return MessageTypeHelper<T>::Set(*_message, _ref, _field, val, _index);
    }
    template<class T>
    typename MessageTypeHelper<T>::ValueType Get() const
    {
        return MessageTypeHelper<T>::Get(*_message, _ref, _field, _index);
    }
    string ToString() const
    {
        string val;
        switch(_field->cpp_type())
        {
        #define _CONVERT(cpptype, api) \
        case google::protobuf::FieldDescriptor::cpptype: \
        { \
            if(_field->is_repeated()) \
            { \
                if(_index != SIZE_EOF) \
                { \
                    val += ArgConvert::ToString(_ref->GetRepeated##api(*_message, _field, _index)); \
                } \
                else \
                { \
                    val += '['; \
                    size_t count = _ref->FieldSize(*_message, _field); \
                    for(size_t i = 0; i < count; ++i) \
                    { \
                        val += ArgConvert::ToString(_ref->GetRepeated##api(*_message, _field, i)); \
                        val += ','; \
                    } \
                    if(count > 0) \
                        val.pop_back(); \
                    val += ']'; \
                } \
            } \
            else \
            { \
                val = ArgConvert::ToString(_ref->Get##api(*_message, _field)); \
            } \
            break; \
        }
            _CONVERT(CPPTYPE_BOOL, Bool);
            _CONVERT(CPPTYPE_INT32, Int32);
            _CONVERT(CPPTYPE_UINT32, UInt32);
            _CONVERT(CPPTYPE_INT64, Int64);
            _CONVERT(CPPTYPE_UINT64, UInt64);
            _CONVERT(CPPTYPE_DOUBLE, Double);
            _CONVERT(CPPTYPE_FLOAT, Float);
        #undef _CONVERT
        case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
        {
            if(_field->is_repeated())
            {
                if(_index != SIZE_EOF)
                {
                    val += _toString(_field, _ref->GetRepeatedString(*_message, _field, _index));
                }
                else
                {
                    val += '[';
                    size_t count = _ref->FieldSize(*_message, _field);
                    for(size_t i = 0; i < count; ++i)
                    {
                        val += _toString(_field, _ref->GetRepeatedString(*_message, _field, i));
                        val += ',';
                    }
                    if(count > 0)
                        val.pop_back();
                    val += ']';
                }
            }
            else
            {
                val = _toString(_field, _ref->GetString(*_message, _field));
            }
            break;
        }
        case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
        {
            const google::protobuf::EnumValueDescriptor* ev = nullptr;
            if(_field->is_repeated())
            {
                if(_index != SIZE_EOF)
                {
                    ev = _ref->GetRepeatedEnum(*_message, _field, _index);
                    val += ev->name();
                }
                else
                {
                    val += '[';
                    size_t count = _ref->FieldSize(*_message, _field);
                    for(size_t i = 0; i < count; ++i)
                    {
                        ev = _ref->GetRepeatedEnum(*_message, _field, i);
                        val += ev->name();
                        val += ',';
                    }
                    if(count > 0)
                        val.pop_back();
                    val += ']';
                }
            }
            else
            {
                ev = _ref->GetEnum(*_message, _field);
                val += ev->name();
            }
            break;
        }
        case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
        {
            const google::protobuf::Message* pMsg = nullptr;
            if(_field->is_repeated())
            {
                if(_index != SIZE_EOF)
                {
                    pMsg = &(_ref->GetRepeatedMessage(*_message, _field, _index));
                    ObjectToString(*pMsg, val);
                }
                else
                {
                    val += '[';
                    size_t count = _ref->FieldSize(*_message, _field);
                    for(size_t i = 0; i < count; ++i)
                    {
                        pMsg = &(_ref->GetRepeatedMessage(*_message, _field, i));
                        ObjectToString(*pMsg, val);
                        val += ',';
                    }
                    if(count > 0)
                        val.pop_back();
                    val += ']';
                }
            }
            else
            {
                pMsg = &(_ref->GetMessage(*_message, _field));
                ObjectToString(*pMsg, val);
            }
            break;
        }
        default:
            break;
        }
        return val;
    }
    //----------------------------------------------------- 
};
//--------------------------------------------------------- 
/// Unknown消息类型
typedef std::shared_ptr<google::protobuf::Message> MessageAny;
//--------------------------------------------------------- 
/**
 * @brief 未知消息接口适配
 * @date 2018-07-20 15:41
 */
class MessageWrapper
{
public:
    typedef MessageMemberItreator<_Nonconst_traits<MessageValue> > Iterator;
    typedef MessageMemberItreator<_Const_traits<MessageValue> > ConstIterator;
protected:
    const google::protobuf::Message* _message;
    const google::protobuf::Descriptor* _descriptor;
public:
    MessageWrapper() : _message(nullptr), _descriptor(nullptr) {}
    MessageWrapper(const google::protobuf::Message& msg) : 
        _message(&msg), _descriptor(msg.GetDescriptor()) {}
    MessageWrapper(const MessageAny& msg)
    {
        if(nullptr != msg)
        {
            _message = msg.get();
            _descriptor = msg->GetDescriptor();
        }
    }
    /// 返回适配的对象是否为nullptr
    inline bool IsNull() const
    {
        return _message == nullptr;
    }
    /// 返回消息属性成员个数
    size_t Size() const
    {
        return _message->GetDescriptor()->field_count();
    }

    /* 返回属性成员迭代器 */
    Iterator Begin()
    {
        return Iterator(*_message, 0, Size());
    }
    ConstIterator Begin() const
    {
        return ConstIterator(*_message, 0, Size());
    }
    Iterator End()
    {
        size_t maxIndex = Size();
        return Iterator(*_message, maxIndex, maxIndex);
    }
    ConstIterator End() const
    {
        size_t maxIndex = Size();
        return ConstIterator(*_message, maxIndex, maxIndex);
    }

    google::protobuf::Message* operator->()
    {
        return const_cast<google::protobuf::Message*>(_message);
    }
    const google::protobuf::Message* operator->() const
    {
        return _message;
    }
    
    MessageValue operator[](size_t number) const
    {
        const google::protobuf::FieldDescriptor* field = _descriptor->FindFieldByNumber(number);
        return MessageValue(*_message, field, _message->GetReflection());
    }
    MessageValue operator[](const string& name) const
    {
        const google::protobuf::FieldDescriptor* field = _descriptor->FindFieldByName(name);
        return MessageValue(*_message, field, _message->GetReflection());
    }
};
//--------------------------------------------------------- 
/**
 * @brief 消息加载器
 * @date 2018-07-24 14:01
 */
class MessageLoader
{
public:
    typedef std::shared_ptr<google::protobuf::compiler::DiskSourceTree> SourceTreeType;
    typedef std::shared_ptr<google::protobuf::compiler::Importer> ImporterType;
    typedef std::shared_ptr<google::protobuf::DynamicMessageFactory> FactoryType;
protected:
    list_t<SourceTreeType> _sourceTreePool;
    list_t<ImporterType> _importerPool;
    list_t<FactoryType> _factoryPool;

    const google::protobuf::FileDescriptor* _importNew(const string& file)
    {
        ByteBuilder sDir;
        ByteArray sFile(file.c_str(), file.length());
        size_t index = StringConvert::LastIndexOf(sFile, PATH_SEPARATOR);
        if(index != SIZE_EOF)
        {
            sDir = sFile.SubArray(0, index);
        }
        sFile = sFile.SubArray(index + 1);

        _sourceTreePool.push_back(SourceTreeType(new google::protobuf::compiler::DiskSourceTree()));
        SourceTreeType& sourceTree = _sourceTreePool.back();
        sourceTree->MapPath("", sDir.GetString());

        _importerPool.push_back(ImporterType(new google::protobuf::compiler::Importer(sourceTree.get(), nullptr)));
        ImporterType impoter = _importerPool.back();

        _factoryPool.push_back(FactoryType(new google::protobuf::DynamicMessageFactory()));
        return impoter->Import(sFile.GetString());
    }
    MessageAny _create(const google::protobuf::Descriptor* descriptor, 
        google::protobuf::MessageFactory* factory)
    {
        auto prototype = factory->GetPrototype(descriptor);
        return MessageAny(prototype->New());
    }
    MessageAny _createFromImport(const string& name)
    {
        auto factory = _factoryPool.begin();
        for(auto importer : _importerPool)
        {
            auto descriptor = importer->pool()->FindMessageTypeByName(name);
            if(nullptr != descriptor)
                return _create(descriptor, factory->get());
            ++factory;
        }
        return MessageAny();
    }
    MessageAny _createFromName(const string& name)
    {
        auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(name);
        if(nullptr == descriptor)
            return MessageAny();
        return _create(descriptor, google::protobuf::MessageFactory::generated_factory());
    }
public:
    /// 查找指定消息名称的描述符,通过描述符可创建对应的消息对象
    const google::protobuf::Descriptor* Find(const string& name)
    {
        for(auto importer : _importerPool)
        {
            auto descriptor = importer->pool()->FindMessageTypeByName(name);
            if(nullptr != descriptor)
                return descriptor;
        }
        return nullptr;
    }
    /// 导入proto文件
    bool Import(const string& file, list_t<string>* pTypeNames = nullptr)
    {
        auto p = _importNew(file);
        if(nullptr == p)
            return false;
        if(pTypeNames != nullptr)
        {
            for(int i = 0; i < p->message_type_count(); ++i)
            {
                pTypeNames->push_back(p->message_type(i)->full_name());
            }
        }
        return true;
    }
    /// 根据指定的消息名称创建实例
    MessageAny Create(const string& name)
    {
        MessageAny msg = _createFromName(name);
        if(nullptr == msg)
            msg = _createFromImport(name);
        return msg;
    }
    /// 创建消息实例并反序列化
    MessageAny Create(const string& name, const ByteArray& buff)
    {
        MessageAny msg = Create(name);
        if(nullptr != msg && !buff.IsEmpty())
        {
            msg->ParseFromArray(buff.GetBuffer(), buff.GetLength());
        }
        return msg;
    }
};
//--------------------------------------------------------- 
} // namespace protobuf_extractor
} // namespace wrapper
} // namespace zhou_yb
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_PROTOBUF_EXTRACTOR_H_
//========================================================= 